#include "log.h"

#define MAX_CALLBACKS 32

/**
 * @brief 定义回调函数结构体。
 */
typedef struct {
  log_LogFn fn; /**< 回调函数 */
  void *udata;  /**< 用户自定义数据 */
} Callback;

/**
 * @brief 定义日志库的全局状态结构体。
 */
static struct {
  void *udata;                       /**< 用户自定义数据 */
  log_LockFn lock;                   /**< 锁回调函数 */
  bool quiet;                        /**< 是否静默 */
  Callback callbacks[MAX_CALLBACKS]; /**< 回调函数数组 */
  LogLevel current_log_level;        /**< 当前日志级别 */
} L;

static const char *level_strings[] = {"DEBUG", "INFO", "ERROR"};

/**
 * @brief 将表示日志级别的字符串转换为相应的日志级别枚举值。
 *
 * @param level_str
 * 指向表示日志级别的字符串，可能的值包括"DEBUG"、"INFO"和"ERROR"。
 * @return
 * 返回相应的日志级别枚举值。如果输入的字符串不匹配任何已定义的日志级别，
 *         则返回默认的日志级别LOG_DEBUG。
 */
static LogLevel string_to_log_level(const char *level_str) {
  if (strcmp(level_str, "DEBUG") == 0) {
    return LOG_LEVEL_DEBUG;
  } else if (strcmp(level_str, "INFO") == 0) {
    return LOG_LEVEL_INFO;
  } else if (strcmp(level_str, "ERROR") == 0) {
    return LOG_LEVEL_ERROR;
  } else {
    return LOG_LEVEL_DEBUG;  // 默认值
  }
}

/**
 * @brief 通用回调函数，用于将日志写入文件。
 * @param ev 指向日志事件的指针。
 */
static void generic_callback(log_Event *ev) {
  char buf[64];
  if (ev->time &&
      strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time) == 0) {
    buf[0] = '\0';  // 如果 strftime 返回 0，表示出错，确保 buf 是一个空字符串
  }
  fprintf(ev->udata, "%s <%s> [%s] %s\n", buf, level_strings[ev->level],
          ev->file, ev->fmt);
  fflush(ev->udata);
}

/**
 * @brief 加锁函数，用于线程安全。
 */
static void lock(void) {
  if (L.lock) {
    L.lock(true, L.udata);
  }
}

/**
 * @brief 解锁函数，用于线程安全。
 */
static void unlock(void) {
  if (L.lock) {
    L.lock(false, L.udata);
  }
}

/**
 * @brief 设置日志锁。
 * @param fn 锁回调函数。
 * @param udata 用户自定义数据。
 */
void log_set_lock(log_LockFn fn, void *udata) {
  L.lock = fn;
  L.udata = udata;
}

/**
 * @brief 设置日志是否静默。
 * @param enable 布尔值，启用或禁用静默模式。
 */
void log_set_quiet(bool enable) { L.quiet = enable; }

/**
 * @brief 添加日志回调函数。
 * @param fn 回调函数。
 * @param udata 用户自定义数据。
 * @return 0表示成功，非0表示失败。
 */
int log_add_callback(log_LogFn fn, void *udata) {
  for (int i = 0; i < MAX_CALLBACKS; i++) {
    if (!L.callbacks[i].fn) {
      L.callbacks[i] = (Callback){fn, udata};
      return 0;
    }
  }
  return -1;
}

/**
 * @brief 将日志添加到文件。
 * @param fp 文件指针。
 * @return 0表示成功，非0表示失败。
 */
int log_add_fp(FILE *fp) {
  if (fp == NULL) {
    return -1;
  }
  return log_add_callback(generic_callback, fp);
}

void log_set_level(LogLevel level) { L.current_log_level = level; }

/**
 * @brief 根据给定的日志级别、发送者和格式化字符串，记录日志事件。
 *
 * @param level_str 日志级别的字符串表示，用于控制日志的重要性。
 * @param sender 日志事件的发送者标识，通常为函数名或文件名。
 * @param fmt 格式化字符串，用于构建日志消息的主体。
 * @param ... 格式化字符串中的变量参数。
 */
void log_log(const char *level_str, const char *sender, const char *fmt, ...) {
  LogLevel level = string_to_log_level(level_str);
  if (level < L.current_log_level) {
    return;
  }

  log_Event ev = {0};
  ev.level = level;
  lock();
  if (!L.quiet) {
    time_t now = time(NULL);
    ev.time = localtime(&now);
    for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
      Callback *cb = &L.callbacks[i];
      va_start(ev.ap, fmt);
      vsnprintf(ev.fmt, sizeof(ev.fmt), fmt, ev.ap);
      va_end(ev.ap);
      snprintf(ev.file, sizeof(ev.file), "%s", sender);
      ev.udata = cb->udata;
      cb->fn(&ev);
    }
  }
  unlock();
}

/**
 * @brief 根据用户、命令和参数构建日志消息，并调用日志函数记录.
 *
 * 该函数接收日志级别字符串、发送者字符串、用户字符串和一个data结构指针作为参数。
 * 它将构建一条包含用户、命令和参数的最终消息，然后调用log_log函数记录这条日志信息。
 *
 * @param level_str 日志级别字符串，用于指示日志的重要性级别.
 * @param sender 发送者字符串，标识日志信息的来源.
 * @param user 用户字符串，表示触发该日志记录的用户.
 * @param data 一个指向data结构的指针，包含命令和参数.
 */
void log_log_with_data(const char *level_str, const char *sender,
                       const char *user, const data *data) {
  char final_msg[4096];
  snprintf(final_msg, sizeof(final_msg), "(%s) %s %s", user, data->command,
           data->args);
  log_log(level_str, sender, "%s", final_msg);
}